package com.artnology.independence.views;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Paint;
import java.awt.Stroke;
import java.awt.geom.Point2D;

import org.apache.commons.collections15.Transformer;
import org.eclipse.swt.widgets.Display;

import com.artnology.independence.model.DVNode;
import com.artnology.independence.model.DVPackage;
import com.artnology.independence.model.DVType;
import com.artnology.independence.model.DependencyGraph;
import com.artnology.independence.model.relationships.DVReference;
import com.artnology.independence.views.jung.DoubleClickMousePlugin;
import com.artnology.independence.views.jung.DynamicSizeVertexRenderer;
import com.artnology.independence.views.jung.PackageColorVertexRenderer;
import com.artnology.independence.views.jung.SpecialModalGraphMouse;

import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.visualization.Layer;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.transform.MutableTransformer;

public class DependencyViewer extends VisualizationViewer<DVNode, DVReference>
		implements IGraphModificationListener {

	private static final long serialVersionUID = 1L;
	private DependencyGraph dependencyGraph;

	public DependencyViewer(DependencyGraph initialGraph) {
		super(new FRLayout<DVNode, DVReference>(initialGraph.getGraph()));
		
		dependencyGraph = initialGraph;
		
		DynamicSizeVertexRenderer<DVNode, DVReference> vlasr = new DynamicSizeVertexRenderer<DVNode, DVReference>(
				getRenderContext());
		getRenderContext().setVertexShapeTransformer(vlasr);
		getRenderer().setVertexRenderer(
				new PackageColorVertexRenderer<DVNode, DVReference>(
						Color.white, true));
		getRenderer().setVertexLabelRenderer(vlasr);
		setVertexToolTipTransformer(new Transformer<DVNode, String>() {

			@Override
			public String transform(DVNode node) {
				if (node instanceof DVPackage) {
					return node.getName();
				} else if (node instanceof DVType) {
					return ((DVType) node).getPackageName();
				} else {
					return "";
				}
			}

		});
		setBackground(Color.WHITE);
		getRenderContext().setVertexLabelTransformer(
				new ToStringLabeller<DVNode>());
		getRenderContext().setEdgeLabelTransformer(
				new ToStringLabeller<DVReference>());

		// Set up a new stroke Transformer for the edges
		Transformer<DVReference, Stroke> edgeStrokeTransformer = new Transformer<DVReference, Stroke>() {
			public Stroke transform(DVReference reference) {
				return reference.getStroke();
			}
		};

		getRenderContext().setEdgeStrokeTransformer(edgeStrokeTransformer);
		getRenderContext().setEdgeDrawPaintTransformer(
			new Transformer<DVReference, Paint>() {
				public Paint transform(DVReference reference) {
					return reference.getEdgeDrawPaint();
				}
	
			});


		SpecialModalGraphMouse<DVNode, DVReference> gm = new SpecialModalGraphMouse<DVNode, DVReference>(this);
		gm.add(new DoubleClickMousePlugin(0, this));
		setGraphMouse(gm);

		createGraphLayout(initialGraph);


	}

	public void translateAndScale() {
		boolean useHeightToScale = getSize().getHeight() < getSize().getWidth();
		double scalingFactor;
		double translateOffset;
		MutableTransformer viewTransformer = getRenderContext()
				.getMultiLayerTransformer().getTransformer(Layer.VIEW);

		MutableTransformer modelTransformer = getRenderContext()
				.getMultiLayerTransformer().getTransformer(Layer.LAYOUT);

		if (useHeightToScale) {
			scalingFactor = getSize().getHeight()
					/ getGraphLayout().getSize().getHeight();
			translateOffset = (getSize().getWidth() - (getGraphLayout()
					.getSize().getWidth() * scalingFactor)) / 2;
			modelTransformer.setTranslate(translateOffset, 0d);
		} else {
			scalingFactor = getSize().getWidth()
					/ getGraphLayout().getSize().getWidth();
			translateOffset = (getSize().getHeight() - (getGraphLayout()
					.getSize().getHeight() * scalingFactor)) / 2;
			modelTransformer.setTranslate(0d, translateOffset);
		}
		viewTransformer.setScale(scalingFactor, scalingFactor,
				new Point2D.Double(0d, 0d));

	}

	public void centerViewerOnNode(DVNode centerNode) {
		MutableTransformer modelTransformer = getRenderContext()
				.getMultiLayerTransformer().getTransformer(Layer.LAYOUT);

		Point2D foundNode = getGraphLayout().transform(centerNode);
		modelTransformer.setTranslate(getCenter().getX() - foundNode.getX(),
				getCenter().getY() - foundNode.getY());
		MutableTransformer viewTransformer = getRenderContext()
				.getMultiLayerTransformer().getTransformer(Layer.VIEW);
		viewTransformer.setScale(1d, 1d, getCenter());
	}

	public void createGraphLayout(DependencyGraph graph) {
		Layout<DVNode, DVReference> layout = new FRLayout<DVNode, DVReference>(
				graph.getGraph());
		layout.setSize(getOptimalLayoutSize(graph.getGraph().getVertexCount()));
		layout.setGraph(graph.getGraph());
		setGraphLayout(layout);
		translateAndScale();
	}
	
	public Graph<DVNode, DVReference> getGraph() {
		return getGraphLayout().getGraph();
	}
	
	public DependencyGraph getDependencyGraph() {
		return dependencyGraph;
	}

	private Dimension getOptimalLayoutSize(int numberOfVerticesToLayout) {
		int numberPixels = (int) (Math.pow(numberOfVerticesToLayout, 0.5) * 200);
		return new Dimension(numberPixels, numberPixels);
	}

	@Override
	public void graphUpdated(final DependencyGraph updatedGraph) {
		Display.getDefault().asyncExec(new Runnable() {
			@Override
			public void run() {
				getGraphLayout().setGraph(updatedGraph.getGraph());
				repaint();
			}

		});
	}
}
